﻿// Shader-Based-Image-Processing (SBIP)
// http://code.google.com/p/sbip/
//
// Copyright © Frank Nagl, 2009-2010
// admin@franknagl.de
//
namespace SBIP
{
    using System.Windows.Forms;
    using System;
    using System.Drawing.Imaging;
    using System.Drawing;
    using System.IO;

    /// <summary>
    /// Main form of the application ShaderBasedImageProcessor.
    /// </summary>
    public partial class ProcessorForm : Form
    {
        private string optionalStartFileName;
        private GroupBox options;
        private readonly GroupBox videoOptions;
        private readonly VideoForm videoForm;
        readonly AutoUpdater updater;
        /// <summary>The SBIP processor, which manages all image processing 
        /// routines.</summary>
        public Processor Processor { get; protected set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="ProcessorForm"/> class.
        /// </summary>
        /// <param name="useUpdater">if set to <c>true</c> the autoupdater is 
        /// used.</param>
        protected ProcessorForm(bool useUpdater)
        {
            InitializeComponent();
            Processor = new Processor();
            options = new GroupBox();
            videoForm = new VideoForm(Processor);
            videoOptions = videoForm.Options;
            videoOptions.Enabled = false;
            Controls.Add(videoOptions);
            Processor.OnDeviceLostEvent += HandleDeviceLost;
            if (useUpdater)
            {
                updater = new AutoUpdater(
                    Program.OptionsFile,
                    "http://franknagl.de/updates/SBIP/update.csv",
                    Program.ProgramPath,
                    Program.ProgramExe,
                    500,
                    60000);
                updater.OnCheckUpdateEvent += UpdateAvailable;
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ProcessorForm"/> class.
        /// </summary>
        public ProcessorForm() 
            : this(true)
        {            
            Initialize(null);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ProcessorForm"/> class.
        /// </summary>
        /// <param name="filename">The as parameter passed image or video 
        /// filename.</param>
        public ProcessorForm(string filename)
            : this(true)
        {
            Initialize(filename);
        }

        /// <summary>
        /// Routine for updating each frame.
        /// </summary>
        /// <param name="firstCall">If first call it has to be set to 
        /// <c>true</c>.</param>
        public void UpdateEachFrame(bool firstCall)
        {
            if (Processor.IsVideo)
            {
                videoForm.UpdateEachFrame(firstCall);
            }
        }

        private void Initialize(string filename)
        {            
            if (filename == null)
            {
                videoOptions.Enabled = false;
                System.Resources.ResourceManager resource =
                    new System.Resources.ResourceManager("SBIP.Resource",
                    System.Reflection.Assembly.GetExecutingAssembly());
                // ReSharper disable AssignNullToNotNullAttribute
                Bitmap bitmap = 
                    new Bitmap((Bitmap)resource.GetObject("dom_erfurt"));
                // ReSharper restore AssignNullToNotNullAttribute

                options.Height = 100; // Hack, for SetPanelSize(..) method
                SetPanelSize(bitmap.Width, bitmap.Height);
                Processor.Begin(bitmap, panel);
                InitOptions(new OriginalForm(Processor).Options);                
            }
            else
            {
                optionalStartFileName = filename;
                FileInfo info = new FileInfo(filename);
                string ext = info.Extension.ToLower();

                if (ext == ".jpg" ||
                    ext == ".jpeg" ||
                    ext == ".bmp" ||
                    ext == ".png" ||
                    ext == ".emf" ||
                    ext == ".gif" ||
                    ext == ".tif" ||
                    ext == ".tiff" ||
                    ext == ".wmf")
                    LoadImage(filename);
                else if (ext == ".avi" ||
                         ext == ".mpeg" ||
                         ext == ".mpg" ||
                         ext == ".mpeg4" ||
                         ext == ".mp4" ||
                         ext == ".divx" ||
                         ext == ".dvx" ||
                         ext == ".xvid")
                    LoadVideo(filename);
                else
                    throw new ArgumentException
                        (@"This file is not processable by SBIP.", "filename");                
            }
        }

        private void HandleDeviceLost()
        {
            Processor.End();
            Initialize(optionalStartFileName);
        }

        private void UpdateAvailable()
        {
            UpdateToolStripMenuItem.Visible = true;
            if (VistaSecurity.IsAdmin())
            {
                updater.MakeUpdate();
            }
            else
            {
                if (MessageBox.Show(
                    @"New version available. Do you want to update (recommended)?",
                    @"New version available :-)",
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    //UpdateToolStripMenuItem_Click(null, null);
                    VistaSecurity.RestartElevated();
                }
            }
        }

        private void SetPanelSize(int w, int h)
        {
            Location = new Point(0, 0);
            Size maxSize = new Size(
                    SystemInformation.PrimaryMonitorSize.Width,
                    SystemInformation.PrimaryMonitorSize.Height - 45);
            int moniW = SystemInformation.PrimaryMonitorSize.Width - 300;
            int moniH = SystemInformation.PrimaryMonitorSize.Height - 240;
            int addWidthForSlider = 0; // slider needs width of 450

            if (moniW < w || moniH < h)
            {
                bool wLonger = 
                    (w / (float)h) > (moniW / (float)moniH) ? true : false;
                if (wLonger)
                {
                    moniH = h * moniW / w;
                    maxSize.Height = moniH + 170 + panel.Location.Y;
                }
                else
                {
                    moniW = w * moniH / h;
                    maxSize.Width = moniW + 300 + panel.Location.X;
                }
                panel.Size = new Size(moniW, moniH);
                Size = maxSize;
            }
            else
            {
                panel.Size = new Size(w, h);
                Size = new Size(
                    w + 300 + panel.Location.X, 
                    h + 170  + panel.Location.Y);
            }

            // check, if windows is high enough, so complete options are visible
            if (panel.Size.Height < options.Height + 50 + panel.Location.Y)
                Height = options.Height + 200 + panel.Location.Y;
            
            // check, if windows is width enough, so complete video options are visible
            if (panel.Size.Width < 450)
            {
                addWidthForSlider = 450 - panel.Size.Width;
                Width = Width + addWidthForSlider;//options.Height + 300 + panel.Location.X;
            }

            options.Location = new Point(//832, 27);
                panel.Size.Width + addWidthForSlider + 30,
                30);

            videoOptions.Location = new Point(
                panel.Location.X, panel.Location.Y + panel.Size.Height + 20);

            videoOptions.Width = panel.Width + addWidthForSlider;            
        }

        private void InitOptions(GroupBox groupBox)
        {
            Point loc = options.Location;
            Controls.Remove(options);
            options = groupBox;
            options.Location = loc;
            Controls.Add(options);
        }

        private void LoadImage(string imageFilename)
        {
            videoOptions.Enabled = false;
            optionalStartFileName = imageFilename;
            if (Processor.HasBegun)
                Processor.End();
            Bitmap bitmap = new Bitmap(imageFilename);
            SetPanelSize(bitmap.Width, bitmap.Height);
            Processor.Begin(bitmap, panel);
            InitOptions(new OriginalForm(Processor).Options);
        }

        private void LoadVideo(string videoFilename)
        {
            optionalStartFileName = videoFilename;
            videoOptions.Enabled = true;
            try
            {
                if (Processor.HasBegun)
                    Processor.End();
                Size videoSize =
                    DirectShowLib.VideoPlayer.GetVideoSize(
                    videoFilename);
                SetPanelSize(videoSize.Width, videoSize.Height);

                Processor.Begin(videoFilename, panel);
                UpdateEachFrame(true);
                InitOptions(new OriginalForm(Processor).Options);
            }
            catch (Exception)
            {
                optionalStartFileName = null;
                videoOptions.Enabled = false;
                //throw new Exception
                //    ("This video is not playable. Maybe codec is missing.");
                MessageBox.Show(
                    @"This video is not playable. Maybe codec is missing.",
                    @"Video is not playable.", 
                    MessageBoxButtons.OK, 
                    MessageBoxIcon.Error);
                HandleDeviceLost();
            }
        }

        private void UpdateToolStripMenuItemClick(object sender, EventArgs e)
        {
            VistaSecurity.RestartElevated();
        }

        #region Open-Save-Close GUI events
        private void ProcessorFormFormClosing(object sender, FormClosingEventArgs e)
        {
            Processor.End();
        }

        private void SaveAsToolStripMenuItemClick(object sender, EventArgs e)
        {            
            saveFileDialog.Filter = @"PNG (*.png)|*.png";
            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                Bitmap bitmap = Processor.RenderToBitmap();
                bitmap.Save(saveFileDialog.FileName, ImageFormat.Png);
            }
        }

        private void TsItmOpenImageClick(object sender, EventArgs e)
        {
            openFileDialog.Filter = 
                @"Image Files(*.jpg;*.jpeg;*.bmp;*.png;*.emf;*.gif;*.tif;*.tiff;" +
                @"*.wmf)|*.jpg;*.jpeg;*.bmp;*.png;*.emf;*.gif;*.tif;*.tiff;*.wmf";
            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                LoadImage(openFileDialog.FileName);
            }
        }

        private void TsItmOpenVideoClick(object sender, EventArgs e)
        {
            //openFileDialog.Filter = "PNG (*.png)|*.png";
            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                LoadVideo(openFileDialog.FileName);
            }
        }

        private void QuitToolStripMenuItemClick(object sender, EventArgs e)
        {
            Close();
        }
        #endregion Open-Save-Close GUI events

        #region Filter ToolStrip MenuItem Events
        private void NoFilterToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new OriginalForm(Processor).Options);
        }

        private void ChessboardToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new ChessboardForm(Processor).Options);
        }

        private void ExtractChannelToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new ExtractChannelForm(Processor).Options);
        }

        private void InvertToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new InvertForm(Processor).Options);
        }

        private void GrayscaleToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new GrayscaleForm(Processor).Options);
        }

        private void LaplaceToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new LaplaceForm(Processor).Options);
        }

        private void RotateChannelToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new RotateChannelsForm(Processor).Options);
        }

        private void SepiaToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new SepiaForm(Processor).Options);
        }

        private void ThresholdToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new ThresholdForm(Processor).Options);
        }

        private void ThresholdRgbToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new ThresholdRGBForm(Processor).Options);
        }

        private void SobelToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new SobelForm(Processor).Options);
        }
        #endregion Filter ToolStrip MenuItem Events        

        #region Drag and drop events
        private void ProcessorFormDragDrop(object sender, DragEventArgs dea)
        {
            if (dea.Data.GetDataPresent(DataFormats.FileDrop))
            {
                string[] files = (string[])dea.Data.GetData(DataFormats.FileDrop);
                Initialize(files[0]); // only first element of drag objects
            }
        }

        private void ProcessorFormDragOver(object sender, DragEventArgs dea)
        {
            if (dea.Data.GetDataPresent(DataFormats.FileDrop))
                dea.Effect = DragDropEffects.Move;
        }
        #endregion Drag and drop events
    }
}
